---
title: Architecture
summary: Component architecture, layer diagram, and design constraints for the OnBaseKeywordSync console application.
status: draft
owner: Documents
last_reviewed: 2026-09-17
---

# Architecture

OnBaseKeywordSync is a single-project executable: a `SaifStandardProgramBase` runner constructs an Autofac container, resolves the `OnBaseKeywordSyncRunner` application, and the application constructs its own collaborators with `new` rather than resolving them from the container. There is no unit-of-work/database layer — every integration is a remote HTTP or SOAP call.

## Layer diagram

```mermaid
flowchart TD
    MAIN[Main entry point] --> RUNNER["Program : SaifStandardProgramBase"]
    RUNNER --> ARGS["ArgumentContainer : StandardArgumentContainerBase<br>(no arguments declared)"]
    RUNNER --> DI["Autofac container<br>RegisterAppDependencies"]
    DI --> APP["OnBaseKeywordSyncRunner : ConsoleBase"]
    APP --> IMG["ImagingApiClient<br>JsonHttpClient v1/Integration"]
    APP --> UPD["KeywordUpdater<br>per-record decision logic"]
    UPD --> OBC["OnBaseKeywordSyncClient<br>builds OBE form data"]
    UPD --> HELP["KeywordHelper<br>Y/N flag translation"]
    OBC --> WCF["HylandOutBoundContractClient<br>WCF basicHttpBinding"]
    WCF --> ONBASE["(OnBase Keyword Update SOAP service)"]
    IMG --> IMGAPI["(Imaging API<br>ImageKeywordSyncs endpoints)"]
    APP --> LOG[IConsoleLogService]
    DI --> ENV[IEnvironmentService]
```

- `Main` constructs `Program` and calls `Run()`; the framework builds the argument container, runs argument resolution (a no-op here since no arguments are declared), and builds the Autofac container.
- `RegisterAppDependencies` registers only framework-common services and `IEnvironmentService` / `IConsoleLogService` — no `DatabaseConfig` is registered, so no NHibernate unit-of-work layer exists.
- `OnBaseKeywordSyncRunner.Execute()` constructs `ImagingApiClient`, `KeywordUpdater`, `OnBaseKeywordSyncClient`, and `KeywordHelper` directly with `new`, hand-propagating `IEnvironmentService` and `IConsoleLogService` to each.
- All persisted state lives behind the Imaging API and OnBase SOAP service; this process holds no database connection.

## Component responsibilities

| Component | Type | Responsibility |
| --- | --- | --- |
| `Program` | Runner (`SaifStandardProgramBase<,>`) | Composition root, argument container factory, application factory |
| `ArgumentContainer` | Argument container | Declares (zero) command-line arguments |
| `OnBaseKeywordSyncRunner` | Application (`ConsoleBase<>`) | `Initialize` / `Execute` / `Finish` business logic — the entire run |
| `ImagingApiClient` | Service | REST client for the Imaging API `ImageKeywordSyncs` endpoints |
| `KeywordUpdater` | Service | Per-record orchestration, send/skip decision logic, and console narration |
| `OnBaseKeywordSyncClient` | Service | WCF/SOAP client wrapper that builds and sends the OnBase E-Form payload |
| `KeywordHelper` | Service | Keyword value translation (Y/N flag rules) |
| `HylandOutBoundContractClient` (generated) | WCF proxy | SOAP client for the OnBase keyword-update service |

## Composition root

`Program.RegisterAppDependencies(ContainerBuilder builder)` calls `builder.RegisterCommonDependencies(Environment.UserName, new[] { typeof(Program).Assembly })` with **no `DatabaseConfig`** arguments, so the framework's NHibernate unit-of-work and connection registration paths are skipped entirely†. The class-map assembly array points at the executable's own assembly, which contains no `ClassMap<TEntity>` types, so no mappings are registered.

`builder.Register(x => new LogService(x.Resolve<IEnvironmentService>())).As<ILogService>()` registers `SAIF.Services.LogService` as `ILogService`, but it is never resolved by application code — the application logs only through `IConsoleLogService`.

`builder.ConfigureOpenTelemetry(options => { options.Owner = "Documents"; })` is called after the `LogService` registration, so `LogService` receives no `LoggerProvider` and OpenTelemetry log correlation through it is not wired*.

| Registration | Lifetime | Resolved by | Purpose |
| --- | --- | --- | --- |
| `IEnvironmentService` → `WindowsEnvironmentService` | `InstancePerLifetimeScope`† | `OnBaseKeywordSyncRunner`, `ImagingApiClient`, `KeywordUpdater`, `OnBaseKeywordSyncClient` | Environment API configuration retrieval |
| `IConsoleLogService` → `ConsoleLogService` | `InstancePerLifetimeScope`† | `OnBaseKeywordSyncRunner` (forwarded to `KeywordUpdater`) | Console and file logging surface |
| `ILogService` → `LogService` | `InstancePerDependency` (default) | Not resolved anywhere | Registered but unused |
| OpenTelemetry providers | `SingleInstance`† | Framework internals | Traces/logs export with resource owner `documents`† |

## Design constraints
- No database connection is ever established — `RegisterCommonDependencies` is called with no `DatabaseConfig`, so no `IUnitOfWork` is available to this application*.
- Application collaborators are constructed with `new`, not resolved from Autofac, so the container's role ends at the `OnBaseKeywordSyncRunner` application object.
- No transaction or unit-of-work boundary exists: the Imaging API `MarkProcessed` calls and the OnBase SOAP calls are each independent remote operations with no compensating action on failure*.
- The runner's `TApp` generic argument is the abstract base `ConsoleBase<ArgumentContainer>`, not the concrete `OnBaseKeywordSyncRunner`; the container registers and resolves the application by its base type, with the concrete type supplied only by the `CreateApplication` factory.

## Integration points

| System | Type | Purpose |
| --- | --- | --- |
| Imaging API (`{imagingapiurl}v1/Integration`) | REST (JSON over HTTP) | Read unprocessed keyword syncs, clear invalid person updates, mark records processed |
| OnBase keyword-update service | SOAP (WCF `basicHttpBinding`) | Apply keyword updates to OnBase documents |
| Environment API (`IEnvironmentService`) | Configuration | All runtime settings and both endpoint addresses |
| OpenTelemetry | Telemetry | Traces/logs export with resource owner `documents`† |
| Local file system (`C:\temp\OnBaseKeywordSync_Debug.log`) | Debug-only log tee | Truncated at every process start, written only when a debugger is attached |

## Inherited framework behavior

Lifecycle orchestration, argument parsing, exit-code mapping, and header/footer logging are inherited from `SAIF.Framework.Windows` and are not re-documented here.†

## Related documentation
- [Application index](index.md)
- [Overview](overview.md)
- [Run lifecycle](run-lifecycle.md)
- [Arguments](arguments.md)
- [Dependencies](dependencies.md)
- [Configuration](configuration.md)
- [Operations and processes](operations-processes.md)
- [Business rules](business-rules.md)
- [Operational runbook](operational-runbook.md)
- [Tech debt](tech-debt.md)

> **Note:** Statements marked with `†` are derived from dependency library documentation via the Legends CLI. Statements marked with `*` were inferred from code structure or contextual evidence.

## Sources
- `OnBaseKeywordSync/Program.cs`
- `OnBaseKeywordSync/OnBaseKeywordSyncRunner.cs`
- `OnBaseKeywordSync/Services/ImagingApiClient.cs`
- `OnBaseKeywordSync/Services/KeywordUpdater.cs`
- `OnBaseKeywordSync/Services/OnBaseKeywordSyncClient.cs`
- `OnBaseKeywordSync/Services/KeywordHelper.cs`
- `OnBaseKeywordSync/Service References/OnBase/Reference.cs`
